﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SQLite;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.CodeCompletion;
using Storm.TextEditor.Languages;
using Storm.TextEditor.Parser.Objects;
using Storm.TextEditor.Parser.XML;
using Moonlite.ExtensionApi.ControlInterfaces;
using Moonlite.Platform.General;
using Moonlite.Platform.Text.File;
namespace Moonlite.Services.CodeCompletion
{
    public class GalaxyCompletionDataProvider : ICompletionDataProvider
    {
        private static string[] UselessCharacters = new string[] {
            "(",
            ")",
            ".",
            "[",
            "]",
            "{",
            "}",
            ",",
            "+",
            "-",
            "*",
            "/",
            "%",
            "="
        };

        public event EventHandler Updated;
        private AndromedaObjectTypeFilter lastFilter = AndromedaObjectTypeFilter.None;
        private CompletionDataCollection constantData = new CompletionDataCollection();
        private CompletionDataCollection lastEnrichmentData = new CompletionDataCollection();
        private CompletionDataCollection visibilityData = new CompletionDataCollection();
        private CompletionDataCollection typeData = new CompletionDataCollection();
        private CompletionDataCollection keywordData = new CompletionDataCollection();
        public bool AllowShow { get; set; }
        public bool UseAllowShow { get { return false; } }
        public ImageList ImageList { get; set; }
        public CompletionDataCollection CompletionData { get; set; }
        public static BackgroundWorker BackgroundWorker { get; set; }
        public static AndromedaParser Parser { get; private set; }
        public static AndromedaStructure LastStructure { get; private set; }
        private static void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                if (Global.Instance.IsCurrentTabPageTextEditor() == true)
                {
                    ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
                    if (textEditor.InnerTextEditor.Document.Count > 0 && textEditor.InnerTextEditor.Document[0].Count > 0)
                    {
                        LastStructure = Parser.Parse(Global.GetAndromedaOutputFolderPath() + "\\" + Global.AndromedaObjectsFileName);
                    }
                }
            }
            catch (Exception exception)
            {
                Logger.Log += exception.Message + Environment.NewLine;
            }
        }

        protected virtual void OnUpdated(EventArgs e)
        {
            if (Updated != null)
            {
                Updated(this, e);
            }
        }

        private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BuildCompletionData();
        }

        public static void Update()
        {
            if (BackgroundWorker.IsBusy == false)
            {
                BackgroundWorker.RunWorkerAsync();
            }
        }

        private void BuildCompletionData()
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null)
            {
                if (!textEditor.InnerTextEditor.CodeCompletionWindow.Visible)
                {
                    CompletionData.Clear();
                    CompletionDataCollection currentSourceData = ProcessCurrentSource();
                    if (currentSourceData != null)
                    {
                        CompletionData.AddRange(currentSourceData);
                        OnUpdated(EventArgs.Empty);
                    }
                }
            }
        }

        private AndromedaSource GetCurrentSource()
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null && LastStructure != null)
            {
                return GalaxyObjectHelper.GetSourceFromFileName(textEditor.InnerTextEditor.FileName, LastStructure);
            }
            return null;
        }

        private CompletionDataCollection ProcessCurrentSource()
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null && LastStructure != null)
            {
                AndromedaSource currentSource = GalaxyObjectHelper.GetSourceFromFileName(textEditor.InnerTextEditor.FileName, LastStructure);
                if (currentSource != null)
                {
                    return ProcessObject(currentSource);
                }

                textEditor.InnerTextEditor.CodeCompletionWindow.UpdateCompletionDataList();
            }

            return null;
        }

        private CompletionDataCollection ProcessOopObjectsOfObject(AndromedaObject parent)
        {
            CompletionDataCollection completionData = new CompletionDataCollection();
            foreach (AndromedaObject @object in parent.Children)
            {
                if (@object is AndromedaObjectClass)
                {
                    AndromedaObjectClass @class = (AndromedaObjectClass)@object;
                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexClass, @class.Name, "class " + @class.Name);
                    data.Tag = GetCode(@class);
                    completionData.Add(data);
                }
                else if (@object is AndromedaObjectEnrichment)
                {
                    AndromedaObjectEnrichment enrichment = (AndromedaObjectEnrichment)@object;
                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, enrichment.Name, "enrichment " + enrichment.Name);
                    data.Tag = GetCode(enrichment);
                    completionData.Add(data);
                }
                else if (@object is AndromedaObjectStruct)
                {
                    AndromedaObjectStruct @struct = (AndromedaObjectStruct)@object;
                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexStruct, @struct.Name, "struct " + @struct.Name);
                    data.Tag = GetCode(@struct);
                    completionData.Add(data);
                }
            }

            return completionData;
        }

        private CompletionDataCollection ProcessObject(AndromedaObject parent)
        {
            return ProcessObject(parent, AndromedaObjectTypeFilter.All, AndromedaObjectVisibilityFilter.All);
        }

        private CompletionDataCollection ProcessObject(AndromedaObject parent, AndromedaObjectVisibilityFilter visibilityFilter)
        {
            return ProcessObject(parent, AndromedaObjectTypeFilter.All, visibilityFilter);
        }

        private CompletionDataCollection ProcessObject(AndromedaObject parent, AndromedaObjectTypeFilter filter)
        {
            return ProcessObject(parent, filter, AndromedaObjectVisibilityFilter.All);
        }

        private CompletionDataCollection ProcessObject(AndromedaObject parent, AndromedaObjectTypeFilter filter, AndromedaObjectVisibilityFilter visibilityFilter)
        {
            CompletionDataCollection completionData = new CompletionDataCollection();
            if (parent != null)
            {
                foreach (AndromedaObject @object in parent.GetAllChildren())
                {
                    if (DoesObjectMatchVisibilityFilter(@object, visibilityFilter))
                    {
                        if (@object is AndromedaObjectClass && (filter & AndromedaObjectTypeFilter.Classes) != 0)
                        {
                            AndromedaObjectClass @class = (AndromedaObjectClass)@object;
                            DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexClass, @class.Name, "class " + @class.Name);
                            data.Tag = GetCode(@class);
                            completionData.Add(data);
                        }
                        else if (@object is AndromedaObjectEnrichment && (filter & AndromedaObjectTypeFilter.Enrichments) != 0)
                        {
                            AndromedaObjectEnrichment enrichment = (AndromedaObjectEnrichment)@object;
                            DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, enrichment.Name, "enrichment " + enrichment.Name);
                            data.Tag = GetCode(enrichment);
                            completionData.Add(data);
                        }
                        else if (@object is AndromedaObjectField && (filter & AndromedaObjectTypeFilter.Fields) != 0)
                        {
                            AndromedaObjectField field = (AndromedaObjectField)@object;
                            DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, field.Name, field.Type + " " + field.Name);
                            data.Tag = GetCode(field);
                            completionData.Add(data);
                        }
                        else if (@object is AndromedaObjectMethod && (filter & AndromedaObjectTypeFilter.Methods) != 0)
                        {
                            AndromedaObjectMethod method = (AndromedaObjectMethod)@object;
                            if (!(method.Parent is AndromedaObjectClass && method.Name == ((AndromedaObjectClass)method.Parent).Name))
                            {
                                DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexMethod, method.Name, method.Type + " " + method.Name + "(" + method.Signature + ")");
                                data.Tag = GetCode(method);
                                completionData.Add(data);
                            }
                        }
                        else if (@object is AndromedaObjectProperty && (filter & AndromedaObjectTypeFilter.Properties) != 0)
                        {
                            AndromedaObjectProperty property = (AndromedaObjectProperty)@object;
                            DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexProperty, property.Name, property.Type + " " + property.Name);
                            data.Tag = GetCode(property);
                            completionData.Add(data);
                        }
                        else if (@object is AndromedaObjectStruct && (filter & AndromedaObjectTypeFilter.Structs) != 0)
                        {
                            AndromedaObjectStruct @struct = (AndromedaObjectStruct)@object;
                            DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexStruct, @struct.Name, "struct " + @struct.Name);
                            data.Tag = GetCode(@struct);
                            completionData.Add(data);
                        }
                    }
                }
            }

            return completionData;
        }

        private bool DoesObjectMatchVisibilityFilter(AndromedaObject @object, AndromedaObjectVisibilityFilter visibilityFilter)
        {
            if (visibilityFilter == AndromedaObjectVisibilityFilter.All)
            {
                return true;
            }

            if (@object is IAndromedaObjectWithVisibility)
            {
                IAndromedaObjectWithVisibility visibilityObject = (IAndromedaObjectWithVisibility)@object;
                if (!string.IsNullOrEmpty(visibilityObject.Visibility))
                {
                    string visibility = visibilityObject.Visibility.ToLower();
                    List<string> expectedVisibility = new List<string>();
                    if ((visibilityFilter & AndromedaObjectVisibilityFilter.Internal) != 0)
                    {
                        expectedVisibility.Add("internal");
                    }

                    if ((visibilityFilter & AndromedaObjectVisibilityFilter.Protected) != 0)
                    {
                        expectedVisibility.Add("protected");
                    }

                    if ((visibilityFilter & AndromedaObjectVisibilityFilter.Public) != 0)
                    {
                        expectedVisibility.Add("public");
                    }

                    if ((visibilityFilter & AndromedaObjectVisibilityFilter.Private) != 0)
                    {
                        expectedVisibility.Add("private");
                    }

                    if (visibility == "default" && (expectedVisibility.Count == 1 && expectedVisibility[0] == "public"))
                    {
                        return true;
                    }

                    foreach (string modifier in expectedVisibility)
                    {
                        if (visibility.Contains(modifier))
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
            return false;
        }

        private static string GetWordTextFromColumn(Row row, int column)
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null)
            {
                Language currentLanguage = LanguageLoader.GetLanguageFrom(textEditor.InnerTextEditor.CurrentLanguage);
                string[] splitRow = row.Text.Split(currentLanguage.Separators.ToCharArray());
                int currentIndex = 0;
                foreach (string word in splitRow)
                {
                    if (column >= currentIndex && column <= currentIndex + word.Length + 1)
                    {
                        return word;
                    }

                    currentIndex += word.Length + 1;
                }
            }

            return null;
        }

        private static string RemoveUselessCharacters(string input)
        {
            Array.ForEach(UselessCharacters, uselessCharacter =>
            {
                int charIndex = input.IndexOf(uselessCharacter);
                if (charIndex > -1)
                {
                    input = input.Substring(0, charIndex);
                }
            });

            return input;
        }

        private static string GetIdentifierName(ITextEditor textEditor)
        {
            textEditor.InnerTextEditor.Caret.CurrentRow.Parse();
            string currentWord = GetWordTextFromColumn(textEditor.InnerTextEditor.Caret.CurrentRow, textEditor.InnerTextEditor.Caret.Position.X);
            if (string.IsNullOrEmpty(currentWord) == false)
            {
                string typeName = currentWord;
                typeName = RemoveUselessCharacters(typeName);
                return typeName;
            }

            return "";
        }

        private static string GetIdentifierTypeCore(string search, AndromedaSource currentSource, ITextEditor textEditor, out bool found)
        {
            AndromedaObject foundObject = null;
            AndromedaObjectField foundField = null;
            foreach (AndromedaObject child in currentSource.GetAllChildren())
            {
                AndromedaObjectOopType oopType = AndromedaObjectOopType.None;
                if (child is AndromedaObjectClass && ((AndromedaObjectClass)child).Name == search)
                {
                    found = true;
                    return search;
                }
                else if (child is AndromedaObjectEnrichment && ((AndromedaObjectEnrichment)child).Name == search)
                {
                    found = true;
                    return search;
                }
                else if (child is AndromedaObjectField && ((AndromedaObjectField)child).Name == search && GalaxyObjectHelper.GetOopObjectFromName(((AndromedaObjectField)child).Name, currentSource, out oopType) != null)
                {
                    if (((AndromedaObjectField)child).Parent is AndromedaObjectMethod)
                    {
                        found = true;
                        return search;
                    }
                    else
                    {
                        foundField = (AndromedaObjectField)child;
                    }
                }
                else if (child is AndromedaObjectProperty && ((AndromedaObjectProperty)child).Name == search && GalaxyObjectHelper.GetOopObjectFromName(((AndromedaObjectProperty)child).Name, currentSource, out oopType) != null)
                {
                    found = true;
                    return search;
                }
                else if (child is AndromedaObjectStruct && ((AndromedaObjectStruct)child).Name == search)
                {
                    found = true;
                    return search;
                }
            }

            if (foundField != null)
            {
                found = true;
                return search;
            }

            if (foundObject == null)
            {
                AndromedaSourceCollection includedSources = new AndromedaSourceCollection();
                foreach (AndromedaSource includedSource in includedSources)
                {
                    bool includedFound = false;
                    string type = GetIdentifierTypeCore(search, includedSource, textEditor, out includedFound);
                    if (includedFound)
                    {
                        found = true;
                        return type;
                    }
                }
            }

            found = false;
            return "";
        }

        private static string GetIdentifierType(ITextEditor textEditor)
        {
            textEditor.InnerTextEditor.Caret.CurrentRow.Parse();
            string currentWord = GetWordTextFromColumn(textEditor.InnerTextEditor.Caret.CurrentRow, textEditor.InnerTextEditor.Caret.Position.X);
            if (string.IsNullOrEmpty(currentWord) == false)
            {
                string typeName = currentWord;
                typeName = RemoveUselessCharacters(typeName);
                bool found = false;
                AndromedaSource source = GalaxyObjectHelper.GetSourceFromFileName(textEditor.InnerTextEditor.FileName, LastStructure);
                if (source != null)
                {
                    string type = GetIdentifierTypeCore(typeName, source, textEditor, out found);
                    if (found) return type;
                }

                return typeName;
            }

            return "";
        }

        public void OnBeforeShow(char pressedChar, out bool cancel)
        {
            cancel = true;
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null)
            {
                CompletionDataCollection environmentData = null;
                cancel = pressedChar == '.' ? DotTyped() : !VerifyTrustedEnvironment(pressedChar, out environmentData);
                if (!textEditor.InnerTextEditor.CodeCompletionWindow.Visible && !cancel && pressedChar != '.')
                {
                    CompletionData.Clear();
                    if (environmentData != null) CompletionData.AddRange(environmentData);
                    textEditor.InnerTextEditor.CodeCompletionWindow.UpdateCompletionDataList();
                }
            }
        }

        public void OnSelectedItemChanged(ICompletionData selectedItem)
        {
            ICodeExplorer codeExplorer = ((ICodeExplorer)((object[])(Global.Instance.GetService(typeof(ICodeExplorer))))[0]);
            if (!(selectedItem.Tag is string))
            {
                IFunctionExplorer functionExplorer = ((IFunctionExplorer)((object[])(Global.Instance.GetService(typeof(IFunctionExplorer))))[0]);
                GalaxyCodeObject galaxyCodeObject = null;
                foreach (object item in functionExplorer.Items)
                {
                    GalaxyCodeObject codeObject = (GalaxyCodeObject)item;
                    if (codeObject.Name == selectedItem.Text)
                    {
                        galaxyCodeObject = codeObject;
                        break;
                    }
                }

                if (galaxyCodeObject == null)
                {
                    return;
                }

                codeExplorer.SetCode(galaxyCodeObject.Code);
            }
            else
            {
                codeExplorer.SetCode((string)selectedItem.Tag);
            }
        }

        public void OnCodeCompleted(char completionCharacter, out bool show)
        {
            show = false;
            if (completionCharacter == '.')
            {
                show = DotTyped();
            }
            else
            {
                AllowShow = false;
                ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
                if (textEditor != null)
                {
                    textEditor.InnerTextEditor.CodeCompletionWindow.Hide();
                }
            }
            lastFilter = AndromedaObjectTypeFilter.None;
        }

        public bool DotTyped()
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null && LastStructure != null)
            {
                string type = GetIdentifierType(textEditor);
                AndromedaSource currentSource = GetCurrentSource();
                if (currentSource != null)
                {
                    AndromedaObjectVisibilityFilter filter = AndromedaObjectVisibilityFilter.Public | AndromedaObjectVisibilityFilter.Internal;
                    AndromedaObjectClass @class = GalaxyObjectHelper.GetCurrentClass(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectEnrichment enrichment = GalaxyObjectHelper.GetCurrentEnrichment(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectStruct @struct = GalaxyObjectHelper.GetCurrentStruct(currentSource, textEditor, currentSource.TagLength);
                    if (type == "this" && (@class != null || enrichment != null || @struct != null))
                    {
                        filter = AndromedaObjectVisibilityFilter.All;
                        if (@class != null)
                        {
                            type = @class.Name;
                        }
                        else if (enrichment != null)
                        {
                            type = enrichment.Name;
                        }
                        else if (@struct != null)
                        {
                            type = @struct.Name;
                        }
                    }
                    AndromedaObjectOopType oopType = AndromedaObjectOopType.None;
                    AndromedaObject oopObject = GalaxyObjectHelper.GetOopObjectFromName(type, LastStructure, out oopType);
                    if (oopObject != null)
                    {
                        goto AddMembers;
                    }
                    else
                    {
                        string name = GetIdentifierName(textEditor);
                        oopType = AndromedaObjectOopType.None;
                        foreach (AndromedaObjectField field in currentSource.GetAllFields())
                        {
                            if (field.Name == name)
                            {
                                oopObject = GalaxyObjectHelper.GetOopObjectFromName(field.Type, LastStructure, out oopType);
                                break;
                            }
                        }
                        if (oopObject == null)
                        {
                            AndromedaObjectMethod currentMethod = GalaxyObjectHelper.GetCurrentMethod(currentSource, textEditor, currentSource.TagLength);
                            AndromedaObjectProperty currentProperty = GalaxyObjectHelper.GetCurrentProperty(currentSource, textEditor, currentSource.TagLength);
                            if (currentMethod != null)
                            {
                                foreach (AndromedaObject @object in currentMethod.Children)
                                {
                                    if (@object is AndromedaObjectField)
                                    {
                                        AndromedaObjectField field = (AndromedaObjectField)@object;
                                        if (field.Name == name)
                                        {
                                            oopObject = GalaxyObjectHelper.GetOopObjectFromName(field.Type, currentSource, out oopType);
                                            break;
                                        }
                                    }
                                }
                                if (oopObject == null)
                                {
                                    foreach (AndromedaObjectField param in currentMethod.Parameters)
                                    {
                                        if (param.Name == name)
                                        {
                                            oopObject = GalaxyObjectHelper.GetOopObjectFromName(param.Type, currentSource, out oopType);
                                            break;
                                        }
                                    }
                                }
                            }
                            else if (currentProperty != null)
                            {
                                foreach (AndromedaObject @object in currentProperty.Children)
                                {
                                    if (@object is AndromedaObjectField)
                                    {
                                        AndromedaObjectField field = (AndromedaObjectField)@object;
                                        if (field.Name == name)
                                        {
                                            oopObject = GalaxyObjectHelper.GetOopObjectFromName(field.Type, currentSource, out oopType);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (oopObject != null)
                        {
                            goto AddMembers;
                        }
                        else
                        {
                            AllowShow = false;
                            textEditor.InnerTextEditor.CodeCompletionWindow.Hide();
                        }
                    }

                AddMembers:
                    if (oopObject != null)
                    {
                        textEditor.InnerTextEditor.CodeCompletionWindow.Reset();
                        CompletionData.Clear();
                        CompletionData.AddRange(ProcessObject(oopObject, filter));
                        textEditor.InnerTextEditor.CodeCompletionWindow.SortItems();
                        if (CompletionData.Count == 0)
                        {
                            AllowShow = false;
                            textEditor.InnerTextEditor.CodeCompletionWindow.Hide();
                        }

                        return true;
                    }
                }
            }
            return false;
        }

        private string GetFirstNonNullWord(ITextEditor textEditor)
        {
            string keywordResult = "";
            int keywordStartIndex = 0;
            string[] keywords = textEditor.InnerTextEditor.Caret.CurrentRow.Text.Split(' ');
            for (int i = keywords.Length - 1; i >= 0; i--)
            {
                string keyword = keywords[i];
                if (keywordStartIndex == textEditor.InnerTextEditor.Selection.Bounds.FirstColumn || keywordStartIndex + keyword.Length == textEditor.InnerTextEditor.Selection.Bounds.FirstColumn)
                {
                    keywordResult = keywords[i].Substring(0, keywords[i].Length);
                    if (string.IsNullOrEmpty(keywordResult) || keywordResult == " ") continue;
                    foreach (string removeCharacter in UselessCharacters)
                    {
                        int charIndex = keywordResult.IndexOf(removeCharacter);
                        if (charIndex > -1)
                            keywordResult = keywordResult.Substring(charIndex + 1);
                    }

                    return keywordResult;
                }

                keywordStartIndex += keyword.Length + 1;
            }

            return "";
        }

        private Word GetLastNonNullWord(ITextEditor textEditor, Word startWord, char pressedChar)
        {
            if (startWord != null)
            {
                if (startWord.Index - 1 > -1)
                {
                    for (int i = startWord.Index - 1; i >= 0; i--)
                    {
                        if (startWord.Row[i] != null && startWord.Row[i].Type == WordType.Word && !string.IsNullOrEmpty(startWord.Row[i].Text) && startWord.Row[i].Text != pressedChar.ToString() && startWord.Row[i].Text != "[" && startWord.Row[i].Text != "]")
                        {
                            return startWord.Row[i];
                        }
                    }
                }
            }

            return null;
        }

        private void AddNativeData(CompletionDataCollection target, AndromedaObjectTypeFilter filter)
        {
            lastFilter = filter;
            SQLiteCommand command = new SQLiteCommand("SELECT * FROM elements ORDER BY Name", ((IFunctionExplorer)((object[])(Global.Instance.GetService(typeof(IFunctionExplorer))))[0]).Connection);
            SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
            DataTable dataTable = new DataTable("elements");
            adapter.Fill(dataTable);
            foreach (DataRow row in dataTable.Rows)
            {
                int imageIndex = -1;
                bool allow = false;
                switch ((long)row.ItemArray[5])
                {
                    case 2:
                    case 1:
                        if ((filter & AndromedaObjectTypeFilter.Methods) != 0)
                        {
                            imageIndex = AndromedaImageIndexes.ImageIndexMethod;
                            allow = true;
                        }

                        break;
                    case 3:
                    case 4:
                        if ((filter & AndromedaObjectTypeFilter.Fields) != 0)
                        {
                            imageIndex = AndromedaImageIndexes.ImageIndexVariable;
                            allow = true;
                        }

                        break;
                    case 5:
                        if ((filter & AndromedaObjectTypeFilter.Enrichments) != 0)
                        {
                            imageIndex = AndromedaImageIndexes.ImageIndexEnrichment;
                            allow = true;
                        }

                        break;
                }

                if (allow)
                {
                    ICompletionData data = new DefaultCompletionData(imageIndex, (string)row.ItemArray[0], "");
                    data.Tag = true;
                    if (imageIndex == AndromedaImageIndexes.ImageIndexEnrichment && !IsKeywordInDataList(data.Text, lastEnrichmentData))
                    {
                        lastEnrichmentData.Add(data);
                    }

                    target.Add(data);
                }
            }
        }

        private void AddMembersOfType(IAndromedaObjectWithType type, CompletionDataCollection data)
        {
            if (type is AndromedaObjectClass)
            {
                AndromedaObjectClass @class = (AndromedaObjectClass)type;
                data.AddRange(ProcessObject((@class)));
                AndromedaObjectClass dummyClass = @class;
                while (dummyClass != null && dummyClass.ValidSuperClass)
                {
                    dummyClass = dummyClass.Extends;
                    data.AddRange(ProcessObject(dummyClass, AndromedaObjectVisibilityFilter.Internal | AndromedaObjectVisibilityFilter.Protected | AndromedaObjectVisibilityFilter.Public));
                }
            }
            else if (type is AndromedaObjectEnrichment)
            {
                data.AddRange(ProcessObject((AndromedaObjectEnrichment)type));
            }
            else if (type is AndromedaObjectStruct)
            {
                data.AddRange(ProcessObject((AndromedaObjectStruct)type));
            }
        }

        private void AddTypes(CompletionDataCollection environmentData)
        {
            foreach (AndromedaObject @object in LastStructure.GetAllChildren())
            {
                if (@object is AndromedaObjectEnrichment)
                {
                    AndromedaObjectEnrichment foundEnrichment = (AndromedaObjectEnrichment)@object;
                    if (!IsKeywordInDataList(foundEnrichment.Name, lastEnrichmentData))
                    {
                        DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, foundEnrichment.Name, "enrichment " + foundEnrichment.Name);
                        data.Tag = GetCode(foundEnrichment);
                        lastEnrichmentData.Add(data);
                    }
                }
                else if (@object is AndromedaObjectClass)
                {
                    AndromedaObjectClass foundClass = (AndromedaObjectClass)@object;
                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexClass, foundClass.Name, "class " + foundClass.Name);
                    data.Tag = GetCode(foundClass);
                    environmentData.Add(data);
                }
                else if (@object is AndromedaObjectStruct)
                {
                    AndromedaObjectStruct foundStruct = (AndromedaObjectStruct)@object;
                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexStruct, foundStruct.Name, "struct " + foundStruct.Name);
                    data.Tag = GetCode(foundStruct);
                    environmentData.Add(data);
                }
            }

            environmentData.AddRange(lastEnrichmentData);
        }

        private void ProcessSources(AndromedaSourceCollection sources)
        {
            ProcessSources(sources, AndromedaObjectTypeFilter.All, AndromedaObjectVisibilityFilter.All);
        }

        private void ProcessSources(AndromedaSourceCollection sources, AndromedaObjectTypeFilter filter)
        {
            ProcessSources(sources, filter, AndromedaObjectVisibilityFilter.All);
        }

        private void ProcessSources(AndromedaSourceCollection sources, AndromedaObjectVisibilityFilter filter)
        {
            ProcessSources(sources, AndromedaObjectTypeFilter.All, filter);
        }

        private CompletionDataCollection ProcessSources(AndromedaSourceCollection sources, AndromedaObjectTypeFilter typeFilter, AndromedaObjectVisibilityFilter visibilityFilter)
        {
            CompletionDataCollection data = new CompletionDataCollection();
            foreach (AndromedaSource source in sources)
            {
                data.AddRange(ProcessObject(source, typeFilter, visibilityFilter));
            }

            return data;
        }

        private bool VerifyTrustedEnvironment(char pressedKey, out CompletionDataCollection environmentData)
        {
            ITextEditor textEditor = Global.Instance.GetCurrentTabPageTextEditor();
            if (textEditor != null && pressedKey != ' ')
            {
                Word currentWord = textEditor.InnerTextEditor.Caret.CurrentWord;
                if (currentWord != null && currentWord.Style != null)
                {
                    string loweredName = currentWord.Style.Name.ToLower();
                    if (loweredName.Contains("string") || loweredName.Contains("char") || loweredName.Contains("comment"))
                    {
                        environmentData = null;
                        return false;
                    }
                }

                Word lastWordObject = GetLastNonNullWord(textEditor, currentWord, pressedKey);
                string lastWord = lastWordObject != null ? lastWordObject.Text : "";
                if (IsKeywordInDataList(lastWord, typeData))
                {
                    environmentData = new CompletionDataCollection();
                    return true;
                }

                if (lastWord == ".")
                {
                    environmentData = null;
                    return true;
                }

                if (lastWord == "typedef" || lastWord == "as")
                {
                    environmentData = null;
                    return true;
                }

                if (IsWordType(lastWord) || lastWord == "void")
                {
                    environmentData = null;
                    return false;
                }

                environmentData = new CompletionDataCollection();
                AndromedaSource currentSource = GetCurrentSource();
                if (currentSource != null)
                {
                    AndromedaObjectClass @class = GalaxyObjectHelper.GetCurrentClass(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectEnrichment enrichment = GalaxyObjectHelper.GetCurrentEnrichment(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectStruct @struct = GalaxyObjectHelper.GetCurrentStruct(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectMethod currentMethod = GalaxyObjectHelper.GetCurrentMethod(currentSource, textEditor, currentSource.TagLength);
                    AndromedaObjectProperty currentProperty = GalaxyObjectHelper.GetCurrentProperty(currentSource, textEditor, currentSource.TagLength);
                    bool caretInClass = @class != null;
                    bool caretInEnrichment = enrichment != null;
                    bool caretInStruct = @struct != null;
                    if (lastWord == "return")
                    {
                        if (currentMethod != null)
                        {
                            foreach (AndromedaObjectField param in currentMethod.Parameters)
                            {
                                DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, param.Name, param.Type + " " + param.Name);
                                data.Tag = GetCode(param);
                                environmentData.Add(data);
                            }

                            foreach (AndromedaObject @object in currentMethod.Children)
                            {
                                if (@object is AndromedaObjectField)
                                {
                                    AndromedaObjectField field = (AndromedaObjectField)@object;
                                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, field.Name, field.Type + " " + field.Name);
                                    data.Tag = GetCode(field);
                                    environmentData.Add(data);
                                }
                            }

                            environmentData.AddRange(ProcessObject(currentSource, AndromedaObjectTypeFilter.Fields));
                        }
                        else if (currentProperty != null)
                        {
                            foreach (AndromedaObject @object in currentProperty.Children)
                            {
                                if (@object is AndromedaObjectField)
                                {
                                    AndromedaObjectField field = (AndromedaObjectField)@object;
                                    DefaultCompletionData data = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, field.Name, field.Type + " " + field.Name);
                                    data.Tag = GetCode(field);
                                    environmentData.Add(data);
                                }
                            }

                            environmentData.AddRange(ProcessObject(currentSource, AndromedaObjectTypeFilter.Fields));
                        }

                        return true;
                    }

                    if (lastWord == "=")
                    {
                        if (caretInClass || caretInEnrichment || caretInStruct)
                        {
                            environmentData.AddRange(ProcessSources(LastStructure.Sources, AndromedaObjectTypeFilter.Classes | AndromedaObjectTypeFilter.Enrichments | AndromedaObjectTypeFilter.Structs, AndromedaObjectVisibilityFilter.All));
                            if (caretInClass)
                            {
                                AddMembersOfType(@class, environmentData);
                            }
                            else if (caretInEnrichment)
                            {
                                AddMembersOfType(enrichment, environmentData);
                            }
                            else if (caretInStruct)
                            {
                                AddMembersOfType(@struct, environmentData);
                            }

                            if (lastFilter != AndromedaObjectTypeFilter.All)
                            {
                                AddNativeData(environmentData, AndromedaObjectTypeFilter.All);
                            }

                            return true;
                        }

                        return false;
                    }

                    if (lastWord == "new")
                    {
                        environmentData = new CompletionDataCollection();
                        AddTypes(environmentData);
                        return true;
                    }

                    if (lastWord == "(")
                    {
                        bool added = false;
                        if (caretInClass || caretInEnrichment || caretInStruct)
                        {
                            AddTypes(environmentData);
                            if (caretInClass)
                            {
                                AddMembersOfType(@class, environmentData);
                            }
                            else if (caretInEnrichment)
                            {
                                AddMembersOfType(enrichment, environmentData);
                            }
                            else if (caretInStruct)
                            {
                                AddMembersOfType(@struct, environmentData);
                            }

                            added = true;
                            if (lastFilter != (AndromedaObjectTypeFilter.Fields | AndromedaObjectTypeFilter.Properties | AndromedaObjectTypeFilter.Enrichments))
                            {
                                AddNativeData(environmentData, AndromedaObjectTypeFilter.Fields | AndromedaObjectTypeFilter.Properties | AndromedaObjectTypeFilter.Enrichments);
                            }
                        }

                        if (currentMethod != null || currentProperty != null)
                        {
                            environmentData.AddRange(keywordData);
                            if (!added)
                            {
                                if (caretInClass)
                                {
                                    AddMembersOfType(@class, environmentData);
                                }
                                else if (caretInEnrichment)
                                {
                                    AddMembersOfType(enrichment, environmentData);
                                }
                                else if (caretInStruct)
                                {
                                    AddMembersOfType(@struct, environmentData);
                                }

                                if (lastFilter != (AndromedaObjectTypeFilter.Fields | AndromedaObjectTypeFilter.Properties | AndromedaObjectTypeFilter.Enrichments))
                                {
                                    AddNativeData(environmentData, AndromedaObjectTypeFilter.Fields | AndromedaObjectTypeFilter.Properties | AndromedaObjectTypeFilter.Enrichments);
                                }
                            }

                            foreach (AndromedaObjectField param in currentMethod.Parameters)
                            {
                                DefaultCompletionData paramData = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, param.Name, param.Type + " " + param.Name + " (parameter)");
                                paramData.Tag = GetCode(param);
                                environmentData.Add(paramData);
                            }
                        }

                        return true;
                    }

                    if (currentMethod != null || currentProperty != null)
                    {
                        environmentData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "const", ""));
                        environmentData.AddRange(SnippetCompletionDataFactory.CreateSnippetData(SnippetParser.Snippets));
                        environmentData.AddRange(keywordData);
                        environmentData.AddRange(constantData);
                        if (lastFilter != AndromedaObjectTypeFilter.All)
                        {
                            AddNativeData(environmentData, AndromedaObjectTypeFilter.All);
                        }

                        if (currentMethod != null)
                        {
                            environmentData.AddRange(ProcessObject(currentMethod));
                        }
                        else if (currentProperty != null)
                        {
                            environmentData.AddRange(ProcessObject(currentProperty));
                        }

                        AddTypes(environmentData);
                        if (caretInClass)
                        {
                            AddMembersOfType(@class, environmentData);
                        }
                        else if (caretInEnrichment)
                        {
                            AddMembersOfType(enrichment, environmentData);
                        }
                        else if (caretInStruct)
                        {
                            AddMembersOfType(@struct, environmentData);
                        }

                        if (currentMethod != null)
                        {
                            foreach (AndromedaObjectField param in currentMethod.Parameters)
                            {
                                DefaultCompletionData paramData = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, param.Name, param.Type + " " + param.Name);
                                paramData.Tag = GetCode(param);
                                environmentData.Add(paramData);
                            }
                        }

                        return true;
                    }

                    if (IsKeywordInDataList(lastWord, visibilityData) || lastWord == "(" || string.IsNullOrEmpty(lastWord))
                    {
                        if (!string.IsNullOrEmpty(lastWord))
                        {
                            environmentData.AddRange(visibilityData);
                            environmentData.AddRange(typeData);
                        }

                        AddTypes(environmentData);
                        if (lastFilter != AndromedaObjectTypeFilter.Enrichments)
                        {
                            AddNativeData(environmentData, AndromedaObjectTypeFilter.Enrichments);
                        }

                        environmentData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, "void", ""));
                        if (!string.IsNullOrEmpty(lastWord))
                        {
                            return true;
                        }
                    }

                    if ((string.IsNullOrEmpty(lastWord) || lastWord == " ") || currentWord.Index == 0)
                    {
                        environmentData.AddRange(SnippetCompletionDataFactory.CreateSnippetData(SnippetParser.Snippets));
                        environmentData.AddRange(visibilityData);
                        environmentData.AddRange(typeData);
                        if (!caretInClass && !caretInEnrichment && !caretInStruct)
                        {
                            environmentData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexNamespace, "package", ""));
                            environmentData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexSnippet, "import", ""));
                            environmentData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexSnippet, "include", ""));
                        }

                        if (currentMethod != null || currentProperty != null)
                        {
                            AddTypes(environmentData);
                            if (caretInClass)
                            {
                                AddMembersOfType(@class, environmentData);
                            }
                            else if (caretInEnrichment)
                            {
                                AddMembersOfType(enrichment, environmentData);
                            }
                            else if (caretInStruct)
                            {
                                AddMembersOfType(@struct, environmentData);
                            }

                            if (lastFilter != AndromedaObjectTypeFilter.All)
                            {
                                AddNativeData(environmentData, AndromedaObjectTypeFilter.All);
                            }

                            foreach (AndromedaObjectField param in currentMethod.Parameters)
                            {
                                DefaultCompletionData paramData = new DefaultCompletionData(AndromedaImageIndexes.ImageIndexVariable, param.Name, param.Type + " " + param.Name);
                                paramData.Tag = GetCode(param);
                                environmentData.Add(paramData);
                            }
                        }

                        return true;
                    }
                }
            }

            if (textEditor.InnerTextEditor.CodeCompletionWindow.Visible)
            {
                environmentData = new CompletionDataCollection();
                return true;
            }

            environmentData = null;
            return false;
        }

        public void OnWindowHidden()
        {
            lastFilter = AndromedaObjectTypeFilter.None;
        }

        private bool IsWordType(string word)
        {
            if (LastStructure != null)
            {
                foreach (AndromedaObject @object in LastStructure.GetAllChildren())
                {
                    if (@object is IAndromedaObjectWithType && @object is IAndromedaObjectWithName && ((IAndromedaObjectWithName)@object).Name == word)
                    {
                        return true;
                    }
                }
            }

            if (lastEnrichmentData != null)
            {
                foreach (ICompletionData data in lastEnrichmentData)
                {
                    if (data.Text == word)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private bool IsKeywordInDataList(string keyword, CompletionDataCollection data)
        {
            foreach (ICompletionData completionData in data)
            {
                if (completionData.Text == keyword)
                {
                    return true;
                }
            }

            return false;
        }

        private void SetupStaticCompletionData()
        {
            visibilityData.Clear();
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "public", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "private", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "protected", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "static", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "final", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "abstract", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "const", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "native", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "override", ""));
            visibilityData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexModifier, "virtual", ""));

            typeData.Clear();
            typeData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexClass, "class", ""));
            typeData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, "enrichment", ""));
            typeData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexEnrichment, "enrich", ""));
            typeData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexStruct, "struct", ""));

            keywordData.Clear();
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "break", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "case", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "catch", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "continue", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "default", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "delete", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "do", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "inline", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "instanceof", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "is", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "iskey", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "keyof", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "new", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "return", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "set", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "setinstancelimit", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "super", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "switch", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "this", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "throws", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "throw", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "transient", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "typedef", ""));
            keywordData.Add(new DefaultCompletionData(AndromedaImageIndexes.ImageIndexKeyword, "uses", ""));
        }

        private AndromedaSource GetSourceOfObject(AndromedaObject @object)
        {
            object current = @object;
            while (current != null && !(current is AndromedaSource))
            {
                current = ((AndromedaObject)current).Parent;
            }

            return current is AndromedaSource ? (AndromedaSource)current : null;
        }

        private string GetCode(AndromedaObject @object)
        {
            try
            {
                AndromedaSource source = GetSourceOfObject(@object);
                string text = "";
                using (StreamReader reader = new StreamReader(source.Path))
                {
                    text = reader.ReadToEnd();
                }

                int realOffset = @object.Offset - source.TagLength;
                if (realOffset > -1 && realOffset < text.Length && realOffset + @object.Length < text.Length)
                {
                    return text.Substring(realOffset, @object.Length);
                }
            }
            catch (IOException) { }
            return "";
        }

        public CompletionDataCollection CreateCompletionData()
        {
            AllowShow = true;

            BackgroundWorker.DoWork += OnBackgroundWorkerDoWork;
            BackgroundWorker.RunWorkerCompleted += OnBackgroundWorkerRunWorkerCompleted;

            SetupStaticCompletionData();
            ImageList = AndromedaImageIndexes.GlobalImageList;
            CompletionData = new CompletionDataCollection();
            return CompletionData;
        }

        static GalaxyCompletionDataProvider()
        {
            BackgroundWorker = new BackgroundWorker();
            Parser = new AndromedaParser();
        }
    }
}
